﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ParallelRuntimeLibrary.Internal;
using System.Threading;

namespace ParallelRuntimeLibrary.Collections
{
    public class Combinable<T> 
    {

        Func<T> mFactory;
        LightLockFreeKeyValueList<T> mData;
        public delegate void CombineAction<T1, T2>(ref T1 arg1, T2 arg2);

        /*///////////////////////////////////////////////////////////////////////////////////////////////*/

        public Combinable(): this(NativeMethod.GetProcessorCount(), null)
        {

        }

        public Combinable(int initialSize, Func<T> factory)
        {
            mData = new LightLockFreeKeyValueList<T>(initialSize + 2);
            mFactory = factory;
        }

        /*///////////////////////////////////////////////////////////////////////////////////////////////*/

        /// <summary>
        /// Get or Set the thread local object, note you shouldn't Set the thread local object before get it.
        /// </summary>
        public T Local
        {
            get
            {
                //lock (mData)
                //{
                    T tmp;
                    if (mData.TryGetValue(Thread.CurrentThread.ManagedThreadId, out tmp))
                        return tmp;
                    else
                    {
                        tmp = mFactory != null ? mFactory() : Activator.CreateInstance<T>();
                        mData.Add(Thread.CurrentThread.ManagedThreadId, tmp);
                        return tmp;
                    }
                //}
            }
            set
            {
                //lock (mData)
                //{
                    mData[Thread.CurrentThread.ManagedThreadId] = value;
                //}
            }
        }

        /*///////////////////////////////////////////////////////////////////////////////////////////////*/


        public virtual T Combine(CombineAction<T, T> combine)
        {
            return Combine(combine, null);
        }

        public virtual T Combine(CombineAction<T, T> combine, Func<T> resultFactory)
        {
            T result = resultFactory != null ? resultFactory() : Activator.CreateInstance<T>();

            for (int i = 0; i < mData.mCount; i++)
            {
                if (mData.mArray[i].Value != null)
                    combine(ref result, mData.mArray[i].Value);
            }

            return result;
        }

        public virtual T Combine(Action<T, T> combine)
        {
            T result = mFactory != null ? mFactory() : Activator.CreateInstance<T>();

            for (int i = 0; i < mData.mCount; i++)
            {
                if (mData.mArray[i].Value != null)
                    combine(result, mData.mArray[i].Value);
            }

            return result;
        }

        /// <summary>
        /// Get The threads data, this method will fit for Ahared nothing architecture
        /// </summary>
        public KeyValuePair<int, T>[] GetThreadsData()
        {
            KeyValuePair<int, T>[] toReturn = new KeyValuePair<int, T>[mData.mCount];
            for (int i = 0; i < mData.mCount; i++)
            {
                if (mData.mArray[i].Value != null)
                    toReturn[i] = mData.mArray[i];
            }

            return toReturn;
        }

    }
}
